#include "AspectEngine.h"

namespace agtk {

Scene::Scene(void)
{
	m_bIsLoaded=false;
	m_version.version=AE_LATEST_SCENE_VERSION;
	m_version.revision=AE_LATEST_SCENE_REVISION;
	m_header.max_cameras=1;
	m_header.max_entities=2;
	m_header.max_items=3;
	m_header.max_lights=4;
	m_header.max_objects=5;
	m_header.max_sounds=6;
	m_header.use_fog=true;
	m_header.use_skybox=false;
	m_detail.author="Tom Savage";
	m_detail.name="Test";
	m_bIsLoaded=true;
}

Scene::~Scene(void)
{
}

bool Scene::Save(char *filename)
{	
	// Ensure that the scene is loaded before saving.
	if (m_bIsLoaded) {
		unsigned int total; // Stores total number of objects
		
		FILE* fp; // FILE object.
		fp = fopen(filename, "wb"); // Write using binary format
		if (fp == NULL) {
			g_aspectEngine->GetLogger()->Log(LOGGER_ERROR, "Could not open or create scene file for saving.", filename);
			return false; // If the FILE object is NULL, the save file could not be loaded. Return false (error).
		}

		// Always save using the latest version.
		m_version.version = AE_LATEST_SCENE_VERSION;
		m_version.revision = AE_LATEST_SCENE_REVISION;
		fwrite(&m_version, sizeof(SceneVersion), 1, fp); // Write file version.
		fwrite(&m_header, sizeof(SceneHeader), 1, fp); // Write file header.
		
		//Begin SceneDetail write. All objects containing strings need to be written individually.
		fwrite(&m_detail.starting_position, sizeof(SceneStartingPosition), 1, fp); // Write the starting position.
		WriteString(m_detail.name, fp); // Write the name of the scene.
		WriteString(m_detail.author, fp); // Write the author of the scene.
		
		if (m_header.use_skybox) fwrite (&m_skybox, sizeof(SceneSkybox), 1, fp);
		if (m_header.use_fog) fwrite (&m_fog, sizeof(SceneFog), 1, fp);
		fwrite(&m_environment, sizeof(SceneEnvironment), 1, fp);
	
		// Write the total number of objects.
		total = m_contents.objects->GetTotalElements();
		fwrite(&total, sizeof(unsigned int), 1, fp);
		
		// Write each object.
		m_contents.objects->Iterate(true);
		StoreSceneObject scene_object_store;
		while (m_contents.objects->Iterate() != NULL) {
			m_contents.objects->GetCurrent()->GetDataStore().WriteDataToFile(fp);
		}
	
		fclose(fp);
	
		return true;
	} else {
		return false;
	}
}

bool Scene::Load(char *filename)
{
	m_bIsLoaded = false;
	FILE* fp;
	fp = fopen(filename, "rb");
	if (fp == NULL) {
		g_aspectEngine->GetLogger()->Log(LOGGER_ERROR, "Could not open scene file for loading.", filename);
		return false;
	}

	fread(&m_version, sizeof(SceneVersion), 1, fp);
	fread(&m_header, sizeof(SceneHeader), 1, fp);
	
	//Begin SceneDetail read. All objects containing strings need to be read individually.
	fread(&m_detail.starting_position, sizeof(SceneStartingPosition), 1, fp); // Read the starting position.
	m_detail.name = ReadString(fp); // Read the name of the scene.
	m_detail.author = ReadString(fp); // Read the author of the scene.
	
	if (m_header.use_skybox) fread (&m_skybox, sizeof(SceneSkybox), 1, fp);
	if (m_header.use_fog) fread (&m_fog, sizeof(SceneFog), 1, fp);
	fread(&m_environment, sizeof(SceneEnvironment), 1, fp);

	unsigned int total = 0;
	unsigned int i = 0;

	// Read total number of objects.
	fread(&total, sizeof(unsigned int), 1, fp);

	// If total objects >0, don't load
	if (total > 0) {
		// Reset object list.
		m_contents.objects->Empty();
	
		// Read each object store.
		i = 0;
		StoreSceneObject scene_object_store;
		while (i < total) {
			scene_object_store = StoreSceneObject(fp);
			m_contents.objects->Add(new SceneObject(scene_object_store));
			i++;
		}
	}

	fclose(fp);
	
	m_bIsLoaded = true;

	return true;
}

bool Scene::AddSceneObject(char* name, char* path, bool shared)
{
	if (m_contents.objects->Add(new SceneObject(name,path,shared)))
		return true;
	else return false;
}

bool Scene::AddCamera(Camera* camera, bool makeActive)
{
	if (!m_contents.cameras->Add(camera))
		return false;
	
	if (makeActive)
		m_contents.activeCamera = m_contents.cameras->GetLast();
	
	return true;
}

Camera* Scene::GetActiveCamera(void)
{
	return m_contents.activeCamera;
}

void Scene::Render(void)
{
	if (m_bIsLoaded) {
		if (m_contents.activeCamera) {
			// Probably want to create my own implementation at some point but this will do for now.
			gluLookAt(m_contents.activeCamera->GetPosition()->Get(0),
				  m_contents.activeCamera->GetPosition()->Get(1),
				  m_contents.activeCamera->GetPosition()->Get(2),
				  m_contents.activeCamera->GetFocalPoint()->Get(0),
				  m_contents.activeCamera->GetFocalPoint()->Get(1),
				  m_contents.activeCamera->GetFocalPoint()->Get(2),
				  m_contents.activeCamera->GetUp()->Get(0),
				  m_contents.activeCamera->GetUp()->Get(1),
				  m_contents.activeCamera->GetUp()->Get(2));
		}
		
		m_contents.objects->Iterate(true);
		while (m_contents.objects->Iterate() != NULL) {
			m_contents.objects->GetCurrent()->Render();
		}
	}
}

void Scene::Update(void)
{
	if (m_bIsLoaded) {
		m_contents.objects->Iterate(true);
		while (m_contents.objects->Iterate() != NULL) {
			m_contents.objects->GetCurrent()->Update(m_environment.gravity);
		}
	}
}

}